<template>
  <div class="dg" ref="dgInstance">
    <d-g-header ref="dgHeaderInstance"
                :style="{paddingRight: scrollBarWidth + 'px'}"
                :settings="headerSettings"
                :width="width"
                :root-config="rootConfig"
                @sort-change = "handleSortChange"
                @check-all="handleHeadCheckAllChange"
                @col-resize="handleColResize"></d-g-header>
    <d-g-body ref="dgBodyInstance"
              :leftFixedLeafColumns="leftFixedLeafColumns"
              :rightFixedLeafColumns="rightFixedLeafColumns"
              :normalLeafColumns="normalLeafColumns"
              :width="width"
              :root-config="rootConfig"
              :is-loading="isLoading"
              :data-source="data"
              @check-all="handleNodesCheckAllChange"
              @horizontal-scroll="handleHorizontalScroll"></d-g-body>
    <pagination ref="paginationInstance" v-if="config.pagination"
                :config="rootConfig.pagination"
                :is-loading="isLoading" @start-load-data="handleLoadData"></pagination>
    <column-filter :config="rootConfig" :columns="rootConfig.columns" @column-change="columnChange"></column-filter>
  </div>
</template>

<script>
  import _ from 'lodash'
  import Vue from 'vue'
  import DGHeader from './header/DGHeader.vue'
  import DGBody from './body/DGBody.vue'
  import utils from './utils'
  import Pagination from './pagination/Pagination.vue'
  import Locales from './locales'
  import ColumnFilter from './columnFileter/ColumnFilter.vue'

  let DG_WITH_CHECKING_INTERVAL = 200
  let DG_MIN_COLUMN_WIDTH = 80

  let initColumns = function (columns, parentColumn, leafColumns, groupColumns) {
    let maxDepth = 1
    let firstRightFixedColumn = 0
    for (let idx in columns) {
      if (columns.hasOwnProperty(idx)) {
        columns[idx].parent = parentColumn
        columns[idx]._rowIndex = parentColumn ? parentColumn._rowIndex + 1 : 0
        columns[idx].hide = parentColumn && parentColumn.hide === true || columns[idx].hide === true
        if (columns[idx]._rowIndex + 1 > maxDepth) {
          maxDepth = columns[idx]._rowIndex + 1
        }
        // 上、右、下、左
        columns[idx].borders = [0, 0, 0, 0]
        if (parentColumn) {
          columns[idx].borders[0] = 1
        }
        columns[idx].borders[1] = 1
        // 右边第一层第一列 或有左边框的第一列 左边框
        if (((!parentColumn && columns[idx].fixed === 'right') || (parentColumn && parentColumn.borders[3])) &&
          !firstRightFixedColumn) {
          columns[idx].borders[3] = 1
          firstRightFixedColumn = true
        }
        if (columns[idx].subColumns && columns[idx].subColumns.length > 0) {
          let depth = initColumns(columns[idx].subColumns, columns[idx], leafColumns, groupColumns)
          if (depth > maxDepth && columns[idx].hide !== true) {
            maxDepth = depth
          }
          groupColumns.push(columns[idx])
        } else {
          columns[idx].borders[2] = 1
          leafColumns.push(columns[idx])
        }
      }
    }
    return maxDepth
  }

  let calcColumnLeft = function (columns) {
    let curLeft = 0
    _.forEach(columns, item => {
      item._left = curLeft
      curLeft += item._width
      if (item.subColumns && item.subColumns.length > 0) {
        calcColumnLeft(item.subColumns)
      }
    })
  }

  let calcColumnPosition = function ([
                                       leftFixedColumns,
                                       normalColumns,
                                       rightFixedColumns
                                     ], [
                                       leftFixedLeafColumns,
                                       normalLeafColumns,
                                       rightFixedLeafColumns
                                     ], groupColumns, tableWidth, headerInstance, minColumnWidth, refreshPosition = true) {
    let specWidth = 0
    let noWidthColumns = []
    let allLeafColumns = [].concat(leftFixedLeafColumns,
      normalLeafColumns,
      rightFixedLeafColumns)
    // 如果叶子标题栏设置了width，则宽度_width由width决定
    _.forEach(allLeafColumns, item => {
      if (item.hide) {
        item._width = 0
        return
      } else if (item.width) {
        let width = +item.width || 0
        if (width) {
          specWidth += width
          item._width = width
          return
        }
      }
      noWidthColumns.push(item)
    })
    // 如果指定了宽度的列总宽度 小于 表的显示宽度，则没有指定宽度的叶子标题栏将平分此宽度
    if (noWidthColumns.length > 0) {
      let width
      width = _.max([Math.floor((tableWidth - specWidth) / noWidthColumns.length), minColumnWidth])
      let totalWidth = 0
      _.forEach(noWidthColumns, item => {
        if ((!item.subColumns || item.subColumns.length === 0) && item.checkbox && width < minColumnWidth + 30) {
          item._width = minColumnWidth + 30
        } else {
          item._width = width
        }
        totalWidth += item._width
      })
      if (tableWidth > totalWidth + specWidth) {
        noWidthColumns[noWidthColumns.length - 1]._width += tableWidth - specWidth - totalWidth
      }
    }

    _.forEach(groupColumns, item => {
      let width = 0
      _.forEach(item.subColumns, col => {
        width += col._width || 0
      })
      item._width = width
    })
    calcColumnLeft(leftFixedColumns)
    calcColumnLeft(normalColumns)
    calcColumnLeft(rightFixedColumns)
    if (refreshPosition) {
      _.forEach(groupColumns, item => {
        item._$headerVM && item._$headerVM.refreshPosition()
      })
      _.forEach(allLeafColumns, item => {
        item._$headerVM && item._$headerVM.refreshPosition()
      })
      headerInstance.refreshPosition()
    }
  }

  export default {
    components: {DGHeader, DGBody, Pagination, ColumnFilter},
    props: {
      config: {
        type: Object,
        required: true
      }
    },
    data () {
      return {
        rootConfig: {},
        data: [],
        width: 0,
        height: 0,
        headerSettings: {
          heights: [],
          leftFixedColumns: [],
          rightFixedColumns: [],
          normalColumns: []
        },
        leftFixedLeafColumns: [],
        rightFixedLeafColumns: [],
        normalLeafColumns: [],
        groupColumns: [],
        checkInterval: 0,
        insideContainerStyle: null,
        scrollBarWidth: utils.scrollBarWidth,
        isLoading: false
      }
    },
    computed: {
      locale () {
        return Locales.ZH_CN
      }
    },
    methods: {
      columnChange () {
        this.resize()
      },
      reload () {
        let pageIndex
        let pageSize
        if (this.$refs.paginationInstance) {
          pageIndex = this.$refs.paginationInstance.pageIndex
          pageSize = this.$refs.paginationInstance.pageSize
        }
        return this.handleLoadData(pageIndex, pageSize)
      },
      handleSortChange () {
        this.reload()
      },
      handleLoadData (pageIndex, pageSize) {
        let ds = this.rootConfig.dataSource
        let itemName = this.$el.id + 'sort'
        if (ds instanceof Function) {
          let field = localStorage.getItem(itemName) && JSON.parse(localStorage.getItem(itemName)).field || ''
          let order = localStorage.getItem(itemName) && JSON.parse(localStorage.getItem(itemName)).order || ''
          ds = ds(pageIndex, pageSize, field, order)
        }
        if (ds && ds.then && ds.then instanceof Function) {
          this.isLoading = true
          return ds.then(data => {
            this.data = data.data
            this.$refs.paginationInstance && this.$refs.paginationInstance.loadComplete(data.totalCount)
          })['finally'](() => {
            this.isLoading = false
          })
        } else if (ds instanceof Array) {
          this.data = ds
        } else {
          this.data = []
        }
      },
      initConfig (config) {
        this.width = this.$refs.dgInstance.clientWidth - utils.scrollBarWidth
        this.height = this.$refs.dgInstance.clientHeight
        this.isLoading = false
        if (this.checkInterval) {
          window.clearInterval(this.checkInterval)
        }
        let nConfig = _.cloneDeep(config)
        if (!nConfig.columns) {
          console.error('data-grid must config columns.')
          return
        }
        if (!nConfig.locale) {
          nConfig.locale = this.locale
          nConfig.pagination && (nConfig.pagination.locale = this.locale)
        }
        this.rootConfig = nConfig
        if (!this.rootConfig.minColumnWidth) {
          this.rootConfig.minColumnWidth = DG_MIN_COLUMN_WIDTH
        }
        let leftFixedLeafColumns = []
        let rightFixedLeafColumns = []
        let normalLeafColumns = []
        let newGroupColumns = []
        let depth = _.max([
          initColumns(_.filter(nConfig.columns, item => { return item.fixed === 'left' }), null, leftFixedLeafColumns, newGroupColumns),
          initColumns(
            _.filter(nConfig.columns, item => { return !item.fixed || (item.fixed !== 'left' && item.fixed !== 'right') }),
            null,
            normalLeafColumns,
            newGroupColumns),
          initColumns(_.filter(nConfig.columns, item => { return item.fixed === 'right' }), null, rightFixedLeafColumns, newGroupColumns)])
        let heights = []
        for (let i = depth - 1; i >= 0; i--) {
          heights[i] = nConfig.headerHeights && nConfig.headerHeights[i] || 24
          if (i < depth - 1) {
            heights[i] += heights[i + 1]
          }
        }
        this.headerSettings = {
          heights,
          leftFixedColumns: [],
          rightFixedColumns: [],
          normalColumns: []
        }
        this.leftFixedLeafColumns = leftFixedLeafColumns
        this.rightFixedLeafColumns = rightFixedLeafColumns
        this.normalLeafColumns = normalLeafColumns
        this.groupColumns = newGroupColumns

        _.forEach(nConfig.columns, item => {
          if (item.fixed === 'left') {
            this.headerSettings.leftFixedColumns.push(item)
          } else if (item.fixed === 'right') {
            this.headerSettings.rightFixedColumns.push(item)
          } else {
            this.headerSettings.normalColumns.push(item)
          }
        })

        this.resize(false)

        let lastDGWidth = 0
        Vue.nextTick(() => {
          this.checkInterval = window.setInterval(() => {
            let curWidth = this.$refs.dgInstance.clientWidth - utils.scrollBarWidth
            let curHeight = this.$refs.dgInstance.clientHeight
            if (curWidth !== lastDGWidth) {
              this.width = curWidth
              this.height = curHeight
              this.resize()
              lastDGWidth = curWidth
            }
          }, DG_WITH_CHECKING_INTERVAL)

          this.reload()
        })
      },
      resize (refreshPosition = true) {
        calcColumnPosition([
          this.headerSettings.leftFixedColumns,
          this.headerSettings.normalColumns,
          this.headerSettings.rightFixedColumns
        ], [
          this.leftFixedLeafColumns,
          this.normalLeafColumns,
          this.rightFixedLeafColumns
        ], this.groupColumns, this.width, this.$refs.dgHeaderInstance, this.rootConfig.minColumnWidth, refreshPosition)
        this.$refs.dgBodyInstance.refreshPosition()
      },
      handleColResize () {
        this.resize()
      },
      handleHorizontalScroll (offset) {
        this.$refs.dgHeaderInstance.setNormalColumnsScroll(offset)
      },
      handleHeadCheckAllChange (isAllChecked) {
        this.$refs.dgBodyInstance.setCheckAllstate(isAllChecked)
      },
      handleNodesCheckAllChange (isAllChecked) {
        this.setCheckAllState(isAllChecked)
      },
      setCheckAllState (isAllChecked) {
        _.forEach([].concat(this.leftFixedLeafColumns,
          this.normalLeafColumns,
          this.rightFixedLeafColumns), item => {
          item._$headerVM && item._$headerVM.setCheckAllState(isAllChecked)
        })
      }
    },
    watch: {
      config (val) {
        this.initConfig(val)
      }
    },
    mounted () {
      this.initConfig(this.config)
    },
    updated () {
      Vue.nextTick(() => {
        this.resize()
      })
    },
    destroyed () {
      if (this.checkInterval) {
        window.clearInterval(this.checkInterval)
      }
    }
  }
</script>

<style>
  .dg{
    position: relative;
    width: 100%;
    height: 100%;
    border: solid 1px #DDD;
    overflow-y: auto;
    overflow-x: hidden;
    display: flex;
    flex-direction: column;
  }
  .dg-body{
    flex: 1;
  }
</style>
